package xcore

import (
	"bufio"
	"bytes"
	"context"
	"crypto/md5"
	"fmt"
	"gitee.com/xfrm/middleware/xhttp"
	"github.com/360EntSecGroup-Skylar/excelize"
	"io"
	"io/ioutil"
	"math/rand"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"
	"unicode"
)

func ReadLines(file string, distinct bool) []string {
	bytes, e := ioutil.ReadFile(file)
	if e != nil {
		fmt.Println(e)
		return nil
	}
	idstrs := strings.Split(string(bytes), "\n")
	var dismap map[string]struct{}
	if distinct {
		dismap = make(map[string]struct{}, len(idstrs))
	}
	lines := make([]string, 0, len(idstrs))
	for _, idstr := range idstrs {
		idstr = strings.TrimSpace(idstr)
		if len(idstr) == 0 {
			continue
		}
		if distinct {
			_, ok := dismap[idstr]
			if ok {
				continue
			}
			dismap[idstr] = struct{}{}
		}
		lines = append(lines, idstr)
	}
	return lines
}

func ReadLinesToInt64(file string, distinct bool) []int64 {
	bytes, e := ioutil.ReadFile(file)
	if e != nil {
		fmt.Println(e)
		return nil
	}
	return SplitToInt64(context.Background(), string(bytes), "\n", distinct)
}

/**
读取本地文件，并分割格式化文件内容，支持windows/mac换行 或 英文逗号分割
*/
func ReadAndSplitFormattedLocalFile(ctx context.Context, filePath string, duplicateRemoval bool) ([]string, error) {
	data, e := ioutil.ReadFile(filePath)
	if e != nil {
		return nil, e
	}

	return ReadAndSplitFormattedContent(ctx, data, duplicateRemoval)
}

/**
读取网络文件并分割格式化文件内容，支持windows/mac换行 或 英文逗号分割
*/
func ReadAndSplitFormattedNetFile(ctx context.Context, fileUrl string, duplicateRemoval bool, readFileTimeout int) ([]string, error) {
	timeout := time.Second * 60
	if readFileTimeout > 0 {
		timeout = time.Second * time.Duration(readFileTimeout)
	}
	data, err := xhttp.HttpReqPostOk(fileUrl, nil, timeout)
	if err != nil {
		return nil, err
	}

	return ReadAndSplitFormattedContent(ctx, data, duplicateRemoval)
}
func ReadAndSplitFormattedNetFileTimeout(ctx context.Context, fileUrl string, duplicateRemoval bool, timeout time.Duration) ([]string, error) {
	if timeout <= 0 {
		timeout = time.Minute
	}
	data, err := xhttp.HttpReqGetOk(fileUrl, timeout)
	if err != nil {
		return nil, err
	}

	return ReadAndSplitFormattedContent(ctx, data, duplicateRemoval)
}

/**
对字符串内容分割格式化，支持windows/mac换行 或 英文逗号分割
*/
func ReadAndSplitFormattedContent(ctx context.Context, data []byte, duplicateRemoval bool) ([]string, error) {
	c1 := bytes.Replace(data, []byte("\r\n"), []byte("\n"), -1)
	c2 := bytes.Replace(c1, []byte("\r"), []byte("\n"), -1)
	c3 := bytes.Replace(c2, []byte(","), []byte("\n"), -1)
	c := bytes.Split(c3, []byte("\n"))

	var (
		result []string
		exists = map[string]bool{}
	)
	for _, idx := range c {
		//trim non-printable characters
		clean := strings.Map(func(r rune) rune {
			if unicode.IsPrint(r) {
				return r
			}

			return -1
		}, strings.TrimSpace(string(idx)))

		if len(clean) == 0 {
			continue
		}

		if duplicateRemoval {
			if _, ok := exists[clean]; ok {
				continue
			}

			exists[clean] = true
		}

		result = append(result, clean)
	}

	return result, nil
}

/**
读取本地Excel文件
*/
func ReadLocalExcel(ctx context.Context, filepath string, sheet string, skipHeader bool) ([][]string, error) {
	f, err := excelize.OpenFile(filepath)
	if err != nil {
		fmt.Println(err)
		return nil, err
	}

	var datasets [][]string
	rows := f.GetRows(sheet)
	for idx, row := range rows {
		if skipHeader && idx == 0 {
			continue
		}

		var rowdata []string
		for _, colCell := range row {
			rowdata = append(rowdata, colCell)
		}
		datasets = append(datasets, rowdata)
	}

	return datasets, nil
}

/**
读取网络Excel文件
*/
func ReadNetExcel(ctx context.Context, fileUrl string, sheet string, skipHeader bool, timeoutSeconds int) ([][]string, error) {
	content, err := xhttp.HttpReqGetOk(fileUrl, time.Second*time.Duration(timeoutSeconds))
	if err != nil {
		return nil, err
	}

	var tmpfile = fmt.Sprintf("%d-%d", time.Now().UnixNano(), rand.Int31n(1000))
	err = ioutil.WriteFile(tmpfile, content, os.ModePerm)
	if err != nil {
		return nil, err
	}
	defer func() {
		_ = os.Remove(tmpfile)
	}()

	return ReadLocalExcel(ctx, tmpfile, sheet, skipHeader)
}

//逐行处理文本文件
// breakerFast: 如果某一行处理失败，立刻结束，不再继续往下读
func ProcesLineByLine(filePath string, processor func(line string) error, breakerFast bool) error {
	fileHandle, err := os.Open(filePath)
	if err != nil {
		return err
	}
	defer fileHandle.Close()
	lineReader := bufio.NewReader(fileHandle)
	for {
		line, _, err := lineReader.ReadLine()
		if err == io.EOF {
			break
		}
		er := processor(string(line))
		if breakerFast && er != nil {
			return er
		}
	}
	return nil
}

func ReadLinesV2(file string, distinct bool) []string {
	var dismap map[string]struct{}
	if distinct {
		dismap = make(map[string]struct{})
	}
	lines := make([]string, 0)

	ProcesLineByLine(file, func(idstr string) error {
		idstr = strings.TrimSpace(idstr)
		if len(idstr) == 0 {
			return nil
		}
		if distinct {
			_, ok := dismap[idstr]
			if ok {
				return nil
			}
			dismap[idstr] = struct{}{}
		}
		lines = append(lines, idstr)
		return nil
	}, false)
	return lines
}
func FileMd5(file string) (string, error) {
	f, err := os.Open(file) //打开文件
	if nil != err {
		fmt.Println(err)
		return "", err
	}
	defer f.Close()

	md5Handle := md5.New()         //创建 md5 句柄
	_, err = io.Copy(md5Handle, f) //将文件内容拷贝到 md5 句柄中
	if nil != err {
		fmt.Println(err)
		return "", err
	}
	md := md5Handle.Sum(nil)        //计算 MD5 值，返回 []byte
	md5str := fmt.Sprintf("%x", md) //将 []byte 转为 string
	return md5str, nil
}

func Exists(path string) bool {
	_, err := os.Stat(path)
	if err != nil {
		if os.IsExist(err) {
			return true
		}
		return false
	}
	return true
}

// skip调用栈 跳过的个数
//a->b->c 当前在c中运行，则skip 0 为c， skip 1为，b，skip2 为a
//因此调用该函数，建议都是传1，表示的是调用方，而非当前file_utils.go文件
func CurrentPath(skip int) (dir string, path string) {
	// 获取当前文件的路径
	_, path, _, _ = runtime.Caller(skip)
	dir = filepath.Dir(path)
	return
}
